package com.starview.star.compiler.canonical.compile.cont;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;

import com.starview.platform.data.type.Location;
import com.starview.star.compiler.canonical.compile.CompileContext;
import com.starview.star.compiler.canonical.compile.Continue;
import com.starview.star.compiler.canonical.compile.FrameState;

public class JumpContinue implements Continue
{
  private final LabelNode lbl;
  private boolean triggered = false;

  public JumpContinue(LabelNode lbl)
  {
    this.lbl = lbl;
  }

  @Override
  public boolean isJump()
  {
    return true;
  }

  @Override
  public FrameState cont(FrameState src, Location loc, CompileContext cxt)
  {
    MethodNode mtd = cxt.getMtd();
    InsnList ins = mtd.instructions;

    if (!isUnconditionalJump(ins.getLast().getOpcode())) {
      ins.add(new JumpInsnNode(Opcodes.GOTO, lbl));
      triggered = true;
    }
    return src;
  }

  public boolean isTriggered()
  {
    return triggered;
  }

  public void setTriggered(boolean triggered)
  {
    this.triggered = triggered;
  }

  public LabelNode getJmp()
  {
    return lbl;
  }

  public static boolean isUnconditionalJump(int opcode)
  {
    switch (opcode) {
    case Opcodes.GOTO:
    case Opcodes.RETURN:
    case Opcodes.RET:
    case Opcodes.ARETURN:
    case Opcodes.LRETURN:
    case Opcodes.FRETURN:
    case Opcodes.DRETURN:
      return true;
    default:
      return false;
    }
  }

  public void jumpTarget(InsnList ins, FrameState frame, CompileContext cxt)
  {
    if(triggered){
    AbstractInsnNode in = ins.getLast();
    while (in != null) {
      if (in instanceof JumpInsnNode) {
        JumpInsnNode jump = (JumpInsnNode) in;
        if (jump.label == lbl) {
          in = in.getPrevious();
          ins.remove(jump); // remove redundant jump
          continue;
        }

        break;
      } else if (in instanceof LabelNode)
        in = in.getPrevious();
      else
        break;
    }
    ins.add(lbl);
    }
  }

}
